#!/usr/bin/python3

import sys
import librungame.util
import librungame.runner
import librungame.game.dos


def die(msg):
    print(msg, file=sys.stderr)
    sys.exit(1)


class ArgParser:

    progName = "sc2000tool"

    class ParseError(Exception):
        pass

    @classmethod
    def print_help(cls):
        print('%s is a tool for inspecting and modifying Sim City 2000 saved cities.' % (cls.progName))
        print('   To list saved cities:')
        print('       %s -l' % (cls.progName))
        print('   To inspect a saved city:')
        print('       %s -i <saved-city>' % (cls.progName))
        print('   To modify a saved city:')
        print("       %s -N <new-city-name> <saved-city>                  # Change city name" % (cls.progName))
        print("       %s -Y <new-mayor-name> <saved-city>                 # Change mayor name" % (cls.progName))
        print("       %s -M <int> <saved-city>                            # Set city's amount of money" % (cls.progName))
        print("       %s -T <sports-team> <new-team-name> <saved-city>     # Set name of sports team (sports: football, baseball, soccer, cricket, rugby)" % (cls.progName))
        print('')

    @staticmethod
    def parseHelp():
        if sys.argv[1] == "-h" or sys.argv[1] == "--help":
            return DynObject()
        else:
            return None

    @classmethod
    def parseList(cls):
        if sys.argv[1] != "-l":
            return None

        i = 1
        while i < len(sys.argv):
            if sys.argv[i] == "-l":
                i += 1
                continue
            break
        if i < len(sys.argv):
            raise cls.ParseError("Error: Redundant argument(s) found.")
        return DynObject()

    @classmethod
    def parseInspect(cls):
        if sys.argv[1] != "-i":
            return None

        ret = DynObject()
        ret.city_filename = None

        i = 1
        while i < len(sys.argv):
            if sys.argv[i] == "-i":
                i += 1
                continue
            if ret.city_filename is None:
                ret.city_filename = sys.argv[i]
                i += 1
                continue
            break
        if i < len(sys.argv):
            raise cls.ParseError("Error: Redundant argument(s) found.")
        if ret.city_filename is None:
            raise cls.ParseError("Error: No saved city specified.")
        return ret

    @classmethod
    def parseModify(cls):
        if sys.argv[1] not in ["-N", "-Y", "-M", "-T"]:
            return None

        ret = DynObject()
        ret.new_city_name = None
        ret.new_mayor_name = None
        ret.new_money = None
        ret.new_team_info = {}
        ret.city_filename = None

        i = 1
        bFound = False
        while i < len(sys.argv):
            if sys.argv[i] == "-N":
                if ret.new_city_name is not None:
                    raise cls.ParseError('Error: Multiple "-N" arguments found.')
                if i + 1 >= len(sys.argv):
                    raise cls.ParseError('Error: New city name must be specified after "-N" argument.')
                ret.new_city_name = sys.argv[i + 1]
                i += 2
                bFound = True
                continue
            if sys.argv[i] == "-Y":
                if ret.new_mayor_name is not None:
                    raise cls.ParseError('Error: Multiple "-Y" arguments found.')
                if i + 1 >= len(sys.argv):
                    raise cls.ParseError('Error: New city name must be specified after "-Y" argument.')
                ret.new_mayor_name = sys.argv[i + 1]
                i += 2
                bFound = True
                continue
            if sys.argv[i] == "-M":
                if ret.new_money is not None:
                    raise cls.ParseError('Error: Multiple "-M" arguments found.')
                if i + 1 >= len(sys.argv):
                    raise cls.ParseError('Error: New money value must be specified after "-M" argument.')
                try:
                    ret.new_money = int(sys.argv[i + 1])
                except ValueError:
                    raise cls.ParseError('Error: New money value must be integer.')
                i += 2
                bFound = True
                continue
            if sys.argv[i] == "-T":
                if i + 2 >= len(sys.argv):
                    raise cls.ParseError('Error: Sports team and new team name must be specified after "-T" argument.')
                team_type = sys.argv[i + 1]
                team_name = sys.argv[i + 2]
                if team_type in ret.new_team_info:
                    raise cls.ParseError('Error: Team "%s" specified multiple times.')
                ret.new_team_info[team_type] = team_name
                i += 3
                bFound = True
                continue
            if ret.city_filename is None:
                ret.city_filename = sys.argv[i]
                i += 1
                continue
            break
        if i < len(sys.argv):
            raise cls.ParseError("Error: Redundant argument(s) found.")
        if not bFound:
            raise cls.ParseError("Error: No modify operation specified.")
        if ret.city_filename is None:
            raise cls.ParseError("Error: No saved city specified.")
        return ret


class DynObject:
    pass


if __name__ == "__main__":
    op = None
    try:
        if len(sys.argv) <= 1:
            raise ArgParser.ParseError('No argument specified.')

        args = ArgParser.parseHelp()
        if args is not None:
            ArgParser.print_help()
            sys.exit(0)

        if op is None:
            args = ArgParser.parseList()
            if args is not None:
                op = "list"
        if op is None:
            args = ArgParser.parseInspect()
            if args is not None:
                op = "inspect"
        if op is None:
            args = ArgParser.parseModify()
            if args is not None:
                op = "modify"
        if op is None:
            raise ArgParser.ParseError('Argument(s) invalid.')
    except ArgParser.ParseError as e:
        print(str(e) + ' Use "%s -h" to see usage information.' % (ArgParser.progName), file=sys.stderr)
        sys.exit(1)

    sysInfo = librungame.util.SysInfo()
    game = librungame.game.dos.SimCity2000("/opt/simcity2000")
    runner = librungame.runner.DosBox()
    with librungame.RunGame(sysInfo, "simcity2000", game, runner).context(edit_or_run=True):
        if op == "list":
            cityList = game.list_saved_city_files()
            if len(cityList) > 0:
                print("Saved cities:")
                for c in cityList:
                    print("    %s" % (c))
            else:
                print("No saved city found.")
        elif op == "inspect":
            with game.open_saved_city_file(args.city_filename) as cf:
                print("City name: " + cf.get_city_name())
                print("Mayor name: " + cf.get_mayor_name())
                print("Year founded: %i" % (cf.get_year_founded()))
                print("Current Date: %s" % (cf.get_current_date().strftime("%Y-%m-%d")))
                print("Money: $%s" % "{:,}".format(cf.get_money()))
        elif op == "modify":
            with game.open_saved_city_file(args.city_filename) as cf:
                if args.new_city_name is not None:
                    cf.set_city_name(args.new_city_name)
                if args.new_mayor_name is not None:
                    cf.set_mayor_name(args.new_mayor_name)
                if args.new_money is not None:
                    cf.set_money(args.new_money)
                for teamType, teamName in args.new_team_info.items():
                    teamType = teamType.capitalize()
                    cf.set_sports_team_name(teamType, teamName)
                cf.save()
